package com.aisi.service.impl;

import com.aisi.entity.dto.Account;
import com.aisi.entity.vo.request.ConfirmResetVO;
import com.aisi.entity.vo.request.EmailRegisterVO;
import com.aisi.entity.vo.request.EmailResetVO;
import com.aisi.mapper.AccountMapper;
import com.aisi.service.AccountService;
import com.aisi.utils.Const;
import com.aisi.utils.FlowUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @Author: shenjianZ
 * @Date: 2024/2/14 19:01
 * @Description: CRUD实现
 */

@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Resource
    private AmqpTemplate amqpTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private FlowUtils flowUtils;

    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Account account = findAccountByUsernameOrEmail(username);
        if (account == null)
            throw new UsernameNotFoundException("用户或密码错误");
        return User
                .withUsername(username)
                .password(account.getPassword())
                .roles(account.getRole())
                .build();

    }


    /**
     * @param text
     * @return
     */
    public Account findAccountByUsernameOrEmail(String text) {
        return query()
                .eq("username", text).or()
                .eq("email", text)
                .one();
    }

    /**
     * @param type
     * @param email
     * @param ip
     * @return
     */
    @Override
    public String registerEmailVerifyCode(String type, String email, String ip) {
        synchronized (ip.intern()) {
            if (!verifyLimit(ip))
                return "请求频繁，请稍后再试";
            Random random = new Random();
            int code = random.nextInt(899999) + 100000;
            Map<String, Object> data = Map.of("type", type, "email", email, "code", code);
            amqpTemplate.convertAndSend("mail", data);
            stringRedisTemplate.opsForValue()
                    .set(Const.VERIFY_EMAIL_DATA + email, String.valueOf(code), 3, TimeUnit.MINUTES);
            return null;
        }
    }

    @Override
    public String registerEmailAccount(EmailRegisterVO emailRegisterVO) {
        String email = emailRegisterVO.getEmail();
        String username = emailRegisterVO.getUsername();
        String key = Const.VERIFY_EMAIL_DATA + email;
        String code = stringRedisTemplate.opsForValue().get(key);
        if (code == null) return "请先获取验证码";
        if (!code.equals(emailRegisterVO.getCode())) return "验证码输入错误，请重新输入";
        if(existsAccountByUsername(username)) return "此用户名已被注册";
        if(existsAccountByUsername(emailRegisterVO.getEmail())) return "此邮件已被注册";
        String password = passwordEncoder.encode(emailRegisterVO.getPassword());
        Account account = new Account(null, username, password, email, "user", new Date());
        if (save(account)) {
            stringRedisTemplate.delete(key);
            return null;
        }else {
            return "注册失败，发生错误，请联系管理员";
        }
    }

    @Override
    public String resetConfirm(ConfirmResetVO confirmResetVO) {
        String email = confirmResetVO.getEmail();
        String code = stringRedisTemplate.opsForValue().get(Const.VERIFY_EMAIL_DATA + email);
        if(code == null) return "请先获取验证码";
        if(!code.equals(confirmResetVO.getCode()))
            return "验证码错误，请重新输入";
        return null;
    }

    @Override
    public String resetEmailAccountPassword(EmailResetVO emailResetVO) {
        String email = emailResetVO.getEmail();
        String confirm = resetConfirm(new ConfirmResetVO(email, emailResetVO.getCode()));
        if(confirm != null) return confirm;
        String password = passwordEncoder.encode(emailResetVO.getPassword());
        boolean update = update().eq("email", email).set("password", password).update();
        if(update)
            stringRedisTemplate.delete(Const.VERIFY_EMAIL_DATA+email);
        return update ? null : "更新失败，请联系管理员";
    }

    /**
     * @param ip
     * @return
     */
    private boolean verifyLimit(String ip) {
        String key = Const.VERIFY_EMAIL_LIMIT + ip;
        return flowUtils.limitOnceCheck(key, 60);
    }

    public boolean existsAccountByEmail(String email) {
        return baseMapper.exists(Wrappers.<Account>query().eq("email", email));
    }

    public boolean existsAccountByUsername(String username) {
        return baseMapper.exists(Wrappers.<Account>query().eq("username", username));
    }
}
